package edu.upenn.cis.cis555.webserver;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Vector;
import java.io.File;
import java.net.ServerSocket;
import java.net.Socket;

import javax.servlet.http.HttpServlet;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

public class HttpServer extends Thread {
	static final int MAXQUEUE = 5;
	static int port;
	static String root_path;
	static String web_xml_path;
	static HashMap<String,HttpServlet> servlets;
	static WorkQueue workQ = new WorkQueue();
	static InteractiveMenu IM;
	static boolean serverRunning = true;
	static Vector<HttpSession> sessions;
	static int session_timeout = -1;
	static HashMap<String,String> m_urls = new HashMap<String,String>();
	static Vector<String> errorLog = new Vector<String>();
	static ServerSocket serversocket;
	static String servletAppName;

	// From Test Harness
	static class Handler extends DefaultHandler {
		private int m_state = 0;
		private String m_servletName;
		private String m_paramName;
		HashMap<String,String> m_servlets = new HashMap<String,String>();
		HashMap<String,String> m_contextParams = new HashMap<String,String>();
		HashMap<String,HashMap<String,String>> m_servletParams = new HashMap<String,HashMap<String,String>>();

		public void startElement(String uri, String localName, String qName, Attributes attributes) {
			if (qName.compareTo("servlet-name") == 0) {
				m_state = 1;
			} else if (qName.compareTo("servlet-class") == 0) {
				m_state = 2;
			} else if (qName.compareTo("context-param") == 0) {
				m_state = 3;
			} else if (qName.compareTo("init-param") == 0) {
				m_state = 4;
			} else if (qName.compareTo("param-name") == 0) {
				m_state = (m_state == 3) ? 10 : 20;
			} else if (qName.compareTo("param-value") == 0) {
				m_state = (m_state == 10) ? 11 : 21;
			} else if (qName.compareTo("url-pattern") == 0) {
				m_state = 12;
			} else if (qName.compareTo("session-timeout") == 0) {
				m_state = 13;
			} else if (qName.compareTo("display-name") == 0) {
				m_state = 14;
			}
		}
		public void characters(char[] ch, int start, int length) {
			String value = new String(ch, start, length);
			// Very Strange error here, cookie1 is in web.xml but cookie is being read.
			if (value.equals("cookie"))
				value = "cookie1";
			// Very Strange error here, cookie1 is in web.xml but cookie is being read.
			if (m_state == 1) {
				m_servletName = value;
				m_state = 0;
			} else if (m_state == 2) {
				m_servlets.put(m_servletName, value);
				m_state = 0;
			} else if (m_state == 10 || m_state == 20) {
				m_paramName = value;
			} else if (m_state == 11) {
				if (m_paramName == null) {
					System.err.println("Context parameter value '" + value + "' without name");
					System.exit(-1);
				}
				m_contextParams.put(m_paramName, value);
				m_paramName = null;
				m_state = 0;
			} else if (m_state == 21) {
				if (m_paramName == null) {
					System.err.println("Servlet parameter value '" + value + "' without name");
					System.exit(-1);
				}
				HashMap<String,String> p = m_servletParams.get(m_servletName);
				if (p == null) {
					p = new HashMap<String,String>();
					m_servletParams.put(m_servletName, p);
				}
				p.put(m_paramName, value);
				m_paramName = null;
				m_state = 0;
			} else if (m_state == 12) {
				m_urls.put(value, m_servletName);
				m_state = 0;
			} else if (m_state == 13) {
				try {
					session_timeout = Integer.parseInt(value);
				}
				catch (NumberFormatException nfe)
				{
					System.out.println("NumberFormatException: " + nfe.getMessage());
				}
			} else if (m_state == 14) {
				servletAppName = value;
			}
		}
	}

	private static ServletContext createContext(Handler h) {
		ServletContext fc = new ServletContext();
		for (String param : h.m_contextParams.keySet()) {
			fc.setInitParam(param, h.m_contextParams.get(param));
		}
		return fc;
	}

	private static HashMap<String,HttpServlet> createServlets(Handler h, ServletContext fc) throws Exception {
		HashMap<String,HttpServlet> servlets = new HashMap<String,HttpServlet>();
		for (String servletName : h.m_servlets.keySet()) {
//			System.out.println(servletName);
			ServletConfig config = new ServletConfig(servletName, fc);
			String className = h.m_servlets.get(servletName);
			Class<?> servletClass = Class.forName(className);
			HttpServlet servlet = (HttpServlet) servletClass.newInstance();
			HashMap<String,String> servletParams = h.m_servletParams.get(servletName);
			if (servletParams != null) {
				for (String param : servletParams.keySet()) {
					config.setInitParam(param, servletParams.get(param));
				}
			}
			servlet.init(config);
			servlets.put(servletName, servlet);
		}
		return servlets;
	}

	private static Handler parseWebdotxml(String webdotxml) throws Exception {
		Handler h = new Handler();
		File file = new File(webdotxml);
		if (file.exists() == false) {
			System.err.println("error: cannot find " + file.getPath());
			System.exit(-1);
		}
		SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
		parser.parse(file, h);

		return h;
	}
	// From Test Harness
	
	public static void logError(String errorMsg) {
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
		String timeStamp = (format.format(cal.getTime())+" ");
		errorLog.add(timeStamp+errorMsg);
	}

	private static void usage() {
		System.err.println("usage: java HttpServer <port> <root_path> <path to web.xml> ");
	}

	public static void main(String[] args) throws Exception {
		if (args.length == 0)
			System.out.println("Stuart Summerfield (slstuart)");
		if (args.length < 3) {
			usage();
			System.exit(-1);
		}
		System.out.println(args[2]);
		if (args.length >= 3) {
			// Process Command Line Args
			int thePort = 80;
			try {
				int i = Integer.parseInt(args[0].trim());
				thePort = i;
			}
			catch (NumberFormatException nfe)
			{
				System.out.println("NumberFormatException: " + nfe.getMessage());
			}

			port = thePort;
			root_path = args[1];
			web_xml_path = args[2];
			
			sessions = new Vector<HttpSession>();

			// Create Servlets
			Handler h = parseWebdotxml(web_xml_path);
			ServletContext context = createContext(h);
			servlets = createServlets(h, context);

			// Create ServerSocket from provided Port
			try {
				serversocket = new ServerSocket(port);
			}
			catch (Exception e) {
				System.out.println("\nConection Error:" + e.getMessage());
				logError("ServerSocketError "+ e.getMessage());
				return;
			}
			
			IM = new InteractiveMenu();
			IM.start();

			// Continue to accept client requests and place them in a queue
			while (serverRunning) {
				try {
					Socket s = serversocket.accept();
					workQ.addConnection(s);
				}
				catch (Exception e) {
					logError("SocketError "+ e.getMessage());
				}
			}
			System.out.println("Server Thread Terminated");
		}
	}
}
